package x.ovo.wechat.bot.contact;

import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.util.StrUtil;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import x.ovo.wechat.bot.common.Context;
import x.ovo.wechat.bot.common.contact.ContactManager;
import x.ovo.wechat.bot.common.contact.RetrievalType;
import x.ovo.wechat.bot.common.entity.BatchContactQuery;
import x.ovo.wechat.bot.common.entity.Contact;
import x.ovo.wechat.bot.common.entity.Member;
import x.ovo.wechat.bot.common.entity.event.SystemEvent;
import x.ovo.wechat.bot.common.http.WechatApi;
import x.ovo.wechat.bot.common.util.WechatUtil;

import java.time.Duration;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class DefaultContactManager implements ContactManager {

    @Getter private final Context context;
    private final WechatApi api;
    private final TimedCache<String, Contact> contactChace = CacheUtil.newTimedCache(Duration.ofMinutes(30).toMillis());
    private final TimedCache<String, List<Member>> groupChace = CacheUtil.newTimedCache(Duration.ofMinutes(30).toMillis());
    private final TimedCache<String, Map<String, Member>> groupMemberCache = CacheUtil.newTimedCache(Duration.ofMinutes(30).toMillis());


    public static ContactManager create(Context context) {
        DefaultContactManager manager = new DefaultContactManager(context, context.getApi());
        context.setContactManager(manager);
        manager.contactChace.schedulePrune(Duration.ofMinutes(5).toMillis());
        manager.groupChace.schedulePrune(Duration.ofMinutes(5).toMillis());
        manager.groupChace.schedulePrune(Duration.ofMinutes(5).toMillis());
        new UserNameStrategy(context.getApi());
        new RemarkStrategy(context.getApi(), manager.contactChace.iterator());
        new NickNameStrategy(context.getApi(), manager.contactChace.iterator());
        return manager;
    }

    @Override
    public Contact getContact(RetrievalType type, String key) {
        Contact contact = this.getStratrgy(type).getContact(this.contactChace.iterator(), key);
        contact.setNickName(WechatUtil.format(contact.getNickName()));
        contact.setRemarkName(WechatUtil.format(contact.getRemarkName()));
        contact.setDisplayName(WechatUtil.format(contact.getDisplayName()));
        return contact;
    }

    @Override
    public Contact getContactByUserName(String userName) {
        return this.contactChace.get(userName, () -> this.getContact(RetrievalType.USER_NAME, userName));
    }

    @Override
    public Contact getContactByNickName(String nickName) {
        return this.getContact(RetrievalType.NICK_NAME, nickName);
    }

    @Override
    public Contact getContactByRemarkName(String remarkName) {
        return this.getContact(RetrievalType.REMARK_NAME, remarkName);
    }

    @Override
    public void removeContact(String key) {
        this.contactChace.remove(key);
        STRATRGY_MAP.values().forEach(stratrgy -> stratrgy.removeContact(key));
    }

    @Override
    public void syncRecentContact(Collection<Contact> contacts) {
        if (null == contacts || contacts.isEmpty()) return;
        contacts.forEach(user -> this.contactChace.put(user.getUserName(), user));
    }

    @Override
    public void flushContact() {
        Contact me = this.api.getLoginSession().getContact();
        this.contactChace.put(me.getUserName(), me);
        this.api.getContactList().forEach(user -> this.contactChace.put(user.getUserName(), user));
        log.info("刷新联系人列表完成，共 {} 位联系人", this.contactChace.size());
        this.context.getEventManager().callEvent(new SystemEvent(StrUtil.format("刷新联系人列表完成，共 {} 位联系人", this.contactChace.size())));
    }

    @Override
    public void flushGroup() {
        this.contactChace.forEach(account -> {
            if (account.isGroup()) {
                List<Member> members = this.api.getGroupMemberList(account.getUserName());
                this.groupChace.put(account.getUserName(), members);
                this.groupMemberCache.put(account.getNickName(), members.stream().collect(Collectors.toMap(Member::getNickName, member -> member, (existingMember, newMember) -> existingMember)));
                log.info("已加载群 [{}] 共 {} 成员", account.getNickName(), account.getMemberCount());
            }
        });
    }

    @Override
    public Member getGroupMember(String groupUserName, String memberUserName) {
        List<Member> members = this.groupChace.get(groupUserName);
        if (null == members) {
            members = this.api.getGroupMemberList(groupUserName);
            this.groupChace.put(groupUserName, members);
        }
        return members.stream()
                .filter(member -> memberUserName.equals(member.getUserName()))
                .findFirst()
                .map(member -> {
                    member.setNickName(WechatUtil.format(member.getNickName()));
                    member.setRemarkName(WechatUtil.format(member.getRemarkName()));
                    member.setDisplayName(WechatUtil.format(member.getDisplayName()));
                    return member;
                })
                .orElseGet(() -> {
                    List<BatchContactQuery> list = List.of(BatchContactQuery.builder().EncryChatRoomId(groupUserName).UserName(memberUserName).build());
                    // 查询返回
                    return this.context.getApi().batchGetContactList(list).get(0).getMemberList().get(0);
                });
    }

    @Override
    public Member getGroupMemberByNickName(String groupNickName, String name) {
        Member result = Optional.ofNullable(this.groupMemberCache.get(groupNickName)).orElseGet(() -> {
            Contact group = this.getContactByNickName(groupNickName);
            List<Member> members = Optional.ofNullable(this.groupChace.get(group.getUserName())).orElseGet(() -> this.api.getGroupMemberList(group.getUserName()));
            this.groupMemberCache.put(groupNickName, members.stream().collect(Collectors.toMap(Member::getNickName, member -> member, (existingMember, newMember) -> existingMember)));
            return this.groupMemberCache.get(groupNickName);
        }).get(name);
        if (null != result) {
            result.setNickName(WechatUtil.format(result.getNickName()));
            result.setRemarkName(WechatUtil.format(result.getRemarkName()));
            result.setDisplayName(WechatUtil.format(result.getDisplayName()));
        }
        return result;
    }
}
